#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Command.h"

#include "Class.h"
#include "Error.h"
#include "String.h"
#include "Port.h"
#include "ObjectTable.h"
#include "ET_stdio.h"

// can't undo, causes change, don't delete
static Command ResetUndo("last command", eCmdTypeCantUndo);

// can't undo, no change, don't delete
static Command NoChanges("no changes", eCmdTypeNoChange);

Command *gNoChanges= &NoChanges;
Command *gResetUndo= &ResetUndo;

//---- Command -----------------------------------------------------------------

NewAbstractMetaImpl(Command,Object, (T(CmdNumber), TP(CmdName), TE(type),
								TE(state), TP(gNoChanges), TP(gResetUndo)));

Command::Command(int itsCmdNumber, const char *itsCmdName, CommandType t)
{
	CmdNumber= itsCmdNumber;
	CmdName= itsCmdName;
	type= t;
	state= eCmdStateNew;
}

Command::Command(const char *itsCmdName, CommandType t)
{
	CmdNumber= 9999;
	CmdName= itsCmdName;
	type= t;
	state= eCmdStateNew;
}

void Command::Commit()
{
}

void Command::DoIt()
{
}

void Command::RedoIt()
{
	DoIt();
}

void Command::UndoIt()
{
}

void Command::Done(Command*)
{
}

void Command::Finish()
{
	if (this == gNoChanges || this == gResetUndo)
		return;
	if (state == eCmdStateDone)
		Commit();
	state= eCmdStateFinished;
	delete this;
}

int Command::Perform()
{
	if (this == gNoChanges || this == gResetUndo)
		return 0;
	int cc= 0;

	switch (state) {
	case eCmdStateNew:
		DoIt();
		state= eCmdStateDone;
		cc= 1;
		break;
	case eCmdStateDone:
		UndoIt();
		state= eCmdStateNotDone;
		cc= -1;
		break;
	case eCmdStateNotDone:
		RedoIt();
		state= eCmdStateDone;
		cc= 1;
		break;
	case eCmdStateFinished:
		fprintf(stderr, "Command::Perform: oops!\n");
		break;
	}
	if (type == eCmdTypeNoChange)
		return 0;
	return cc;
}

const char *Command::GetUndoName()
{
	const char *s1= "", *s, *cs= GetName();

	if (type == eCmdTypeCantUndo)
		s1= "Can't ";

	if (state == eCmdStateDone)
		s= "Undo";
	else
		s= "Redo";
		
	if (cs && *cs)
		return form("%s%s %s", s1, s, cs);
	return form("%s%s", s1, s);
}

void Command::TrackConstrain(Point, Point, Point*)
{
}

void Command::TrackFeedback(Point, Point, bool)
{
}

Command *Command::TrackMouse(TrackPhase, Point, Point, Point)
{
	ResetFlag(eCmdMoveEvents);
	return this;
}

void Command::InspectorId(char *buf, int sz)
{
	if (GetName())
		strn0cpy(buf, GetName(), sz);
	else
		Object::InspectorId(buf, sz);
}

